class ASTroCAT_Clonal extends ASTroCAT {
  
  // ASTroCAT extended to have _NPC_ clonal variants in each size class.
  // right now, implements the ks - phi tradeoff discussed by Merico et al. 2009.
  
  int NPC;
  float[] A; // tradeoff parameter: [-1..1]*adaptability across clones within each size class
  FreeParam adapt,shape,dphi;
  
  boolean do_mericoStyleTradeoff = false;
  boolean do_smithFussmanStyleTradeoff = true;
  
  ASTroCAT_Clonal(int Nsize, int NPC) {
    super(Nsize*NPC); // Nsize is the number of unique size classes, NPC the number of clones per size class. NP = Nsize*NPC
    this.NPC = NPC;
    adapt = addParam("adaptability","adapt","",0,0,0.9);
    A = new float[NP];
    for (int i=0; i<NP; i++) A[i] = 0;
    shape = addParam("tradeoff shape","shape","",0.5,0,2);
    shape.quantize(0.05);
    dphi = addParam("defensible grazing fraction","dphi","",1,0,1);
    
    if (do_mericoStyleTradeoff) {
      shape.hidden = true;
      dphi.hidden = true;
    } else if (do_smithFussmanStyleTradeoff) {
      adapt.hidden = true;
    }
  }
  
  
  void updatePrivateParams() {
    // update phytoplankton size and size-dependent params
    for (int i=0; i<NP; i++) {
      int iSize = i / NPC;
      int iClone = i % NPC;
      Pesd[i] = exp(map(iSize, 0, NP/NPC-1, log(PMinEsd.current), log(PMaxEsd.current)));
      if (NPC > 1) {
        if (do_mericoStyleTradeoff) A[i] = map(iClone, 0, NPC-1, -1, 1) * adapt.current;
        if (do_smithFussmanStyleTradeoff) A[i] = map(iClone, 0, NPC-1, 0, 1);
      } else {
        A[i] = 0;
      }
      mu0[i] = mu00.current * pow(Pesd[i] / Pesd0.current, mu0exp.current);
      if (do_smithFussmanStyleTradeoff) mu0[i] *= (1 - A[i]);
      ks[i] = ks0.current * pow(Pesd[i] / Pesd0.current, ksexp.current);
    }
    // zooplankton size and size-dependent params: same as always
    for (int j=0; j<NZ; j++) {
      Zesd[j] = Zesd0.current * pow(Pesd[j] / xpreyopt0.current, 1.0/xpreyoptexp.current); // set Z sizes such that each P is optimal prey for one Z
      I0[j] = I00.current * pow(Zesd[j] / Zesd0.current, I0exp.current);
      xpreyopt[j] = xpreyopt0.current * pow(Zesd[j] / Zesd0.current, xpreyoptexp.current);
    }
    // grazing preferences
    for (int j=0; j<NZ; j++) {
      for (int i=0; i<NP; i++) {
        phiP[i][j] = exp(-sq((log10(Pesd[i])-log10(xpreyopt[j])) / dxprey.current));
        if (do_mericoStyleTradeoff) phiP[i][j] /= (1e-4 + 1 - A[i]);
        if (do_smithFussmanStyleTradeoff) phiP[i][j] *= (1 - dphi.current * pow(A[i],shape.current));
      }
    }
  }
  
  
  float calcUptake(int i, int k) {
    float ksi = ks[i];
    if (do_mericoStyleTradeoff) ksi /= (1e-4 + 1 + A[i]);
    float nutlim = N.current[k] / (ksi + N.current[k]);
    float mu0i = mu0[i];
    float lightlim = alpha.current * PAR.current[k] / sqrt(sq(mu0i) + sq(alpha.current * PAR.current[k]));
    return mu0i * nutlim * lightlim * P[i].current[k];
  }
  
  
  void saveSpecialInfo(Archive archive) {
    super.saveSpecialInfo(archive);
    archive.addNameValuePair("A", A);
  }
  
}
